﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.Mvc;
using Commander.Core.Refactorings.Modifiers;
using Commander.Models.Dashboard;
using Gendarme;
using Gendarme.Framework;
using ICSharpCode.NRefactory;
using ICSharpCode.SharpZipLib.Zip;

namespace Commander.Controllers {
  public class ProjectController : Controller {
        static ProjectModel model;
        static RunModel runModel;
        static DefectDetail defDetail;
        static Defect defect;
        //TODO: add argument to the index action or validate user session to ensure you can't get here from typing in the url

      
    public ActionResult Index() {
      if (model == null) {
        model = TempData["project"] as ProjectModel;
      }
      string path = Server.MapPath("~/Uploads/" + model.OwnerId + "/" + model.Name + "/");
      //string path = Server.MapPath("~/App_Data/");//i have also tested to upload in App_Data and it works fine
      model.ProjectPath = path;
      if (!Directory.Exists(path)) Directory.CreateDirectory(path);

      model.SetFiles(path);
      ViewData.Model = model;
      return View();
    }

    public ActionResult UploadFile() {
      return View();
    }

    [HttpPost]
    public ActionResult UploadFile(HttpPostedFileBase file) {

      if (file.ContentLength > 0) {
        var fileName = Path.GetFileName(file.FileName);
        string dir = Server.MapPath("~/Uploads/" + model.OwnerId + "/" + model.Name + "/");
        

        //string dir = Server.MapPath("~/App_data/");

        if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
        var path = Path.Combine(dir, fileName);
        file.SaveAs(path);
      }

      return RedirectToAction("Index");
    }


    public ActionResult UploadArchive() {
      return View();
    }

    [HttpPost]
    public ActionResult UploadArchive(HttpPostedFileBase file) {
      //will delete all the existing files
      if (file.ContentLength > 0 && file.FileName.EndsWith(".zip")) {
        var fileName = Path.GetFileName(file.FileName);
        string dir = Server.MapPath("~/Uploads/" + model.OwnerId + "/");

        //string dir = Server.MapPath("~/App_Data/");
        if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
        var path = Path.Combine(dir, fileName);
        file.SaveAs(path);

        string extractionPath = Server.MapPath("~/Uploads/" + model.OwnerId + "/" + model.Name + "/");
        //string extractionPath = Server.MapPath("~/App_Data/");

        //I have commented this portion since if there is already a folder of that name and it has some files then this opertation will cause the program to give runtime exception
        /*
        if (System.IO.Directory.Exists(extractionPath))
        {
          System.IO.Directory.Delete(extractionPath);
        }
         */
        //we then unzip the file
        UnZipFile(path);
        //then delete the .zip file
        System.IO.File.Delete(path);

      }

      return RedirectToAction("Index");
    }

    public void UnZipFile(string InputPathOfZipFile) {
      if (System.IO.File.Exists(InputPathOfZipFile)) {
        string baseDirectory = Path.GetDirectoryName(InputPathOfZipFile) + "\\" + model.Name + "\\";

        if (!Directory.Exists(baseDirectory))
          Directory.CreateDirectory(baseDirectory);

        using (ZipInputStream ZipStream = new ZipInputStream(System.IO.File.OpenRead(InputPathOfZipFile))) {
          ZipEntry theEntry;
          while ((theEntry = ZipStream.GetNextEntry()) != null) {
            if (theEntry.IsDirectory) {
              string strNewDirectory = baseDirectory + theEntry.Name + "\\";
              if (!Directory.Exists(strNewDirectory)) {
                Directory.CreateDirectory(strNewDirectory);
              }
            }
            else if (theEntry.IsFile) {
              if (theEntry.Name != "") {
                string name = "";
                if (theEntry.Name.Contains('/')) {
                  string dirPath = Path.GetDirectoryName(theEntry.Name);
                  if (!Directory.Exists(baseDirectory + dirPath))
                    Directory.CreateDirectory(baseDirectory + dirPath);

                  name = theEntry.Name.Replace('/', '\\');
                }
                else {
                  name = theEntry.Name;
                }
                string strNewFile = baseDirectory + name;//.Replace('/','\\');

                if (System.IO.File.Exists(strNewFile)) {
                  continue;
                }

                using (FileStream streamWriter = System.IO.File.Create(strNewFile)) {
                  int size = 2048;
                  byte[] data = new byte[2048];
                  while (true) {
                    size = ZipStream.Read(data, 0, data.Length);
                    if (size > 0)
                      streamWriter.Write(data, 0, size);
                    else
                      break;
                  }
                  streamWriter.Close();
                }
              }
            }
          }
          ZipStream.Close();
        }
      }
    }

      
      public ActionResult Run() {
       
      Process res = null;
      //RunModel runModel = new RunModel();
      runModel = new RunModel();//now we are using a static RunModel
      
      try {

      if (model.IsJava()) {
        throw new NotImplementedException("call Ant");
      }
      else {
        res = Process.Start(Server.MapPath("~/App_Data/nant-0.90/NAnt.exe"), " -t:net-3.5 /f:\"" + model.AntFilePath+"\"");
      }
      while (!res.HasExited) {
        Thread.Sleep(1000);
      }
        string[] args = new string[3];
        args[0] = "--set";
        args[1] = "smells";
        args[2] = model.OutputPath;
        runModel.Defects = new List<Defect>(ConsoleRunner.CommanderExec(args, Server.MapPath("~/") + "\\bin\\"));        
         
        ViewData.Model = runModel;
      }
      catch(Exception ex){
        runModel.Compilation = new RunResult();
        runModel.Compilation.Message = ex.Message;
        runModel.Compilation.Description = ex.StackTrace;
      }
         
      return View();
    }

      public ActionResult Refactor()
      {
          if (defDetail != null)
              ViewData.Model = defDetail;
          string sourceFileName = defect.Source.ToString().Substring(0, defect.Source.IndexOf('('));
          
          StreamReader sr = new StreamReader(sourceFileName);
          string origiContent = sr.ReadToEnd();
          //string origiContent=null;
          //while (sr.Peek() != -1)
          //    origiContent += sr.ReadLine()+"\n";          
          defDetail.SourceCode = origiContent.Replace("\n", "<br/>");

          //Extra should be added inside Gendarme
          
          if (defect.Rule.ToString().Equals("Gendarme.Rules.Smells.AvoidMessageChainsRule"))
          {
              defect.CommanderExtra = new System.Xml.Linq.XElement("messageChain",
                  new System.Xml.Linq.XAttribute("src", defect.Source),
                  new System.Xml.Linq.XAttribute("path", sourceFileName));
          }
          
          
          var parser = ParserFactory.CreateParser(SupportedLanguage.CSharp, new StringReader(origiContent));
          parser.Parse();          
          
          var refactContent = CodeModifiersFactory.CreateModifier(defect.CommanderExtra, parser, defect.Rule).ApplyModification();
          defDetail.RefactoredCode = refactContent.Replace("\n", "<br/>");

          ViewData.Model = defDetail;
          return View();
      }

      
      public ActionResult SmellDetail()
      {           
          //Defect def = TempData["Name"] as Defect;
          int index = int.Parse(Request.QueryString["Index"]);
          defect = runModel.Defects.ElementAt(index);
          
          defDetail = new DefectDetail();
          defDetail.Name = defect.Rule.Name.ToString();
          defDetail.Problem = defect.Rule.Problem.ToString();
          defDetail.Solution = defect.Rule.Solution.ToString();
          defDetail.Location = defect.Location.ToString();
          defDetail.Source = defect.Source.ToString();

          if ((defect.CommanderExtra != null) && (defect.CommanderExtra.Attribute(Gendarme.Rules.Smells.CommanderSerializer.NAME).Value) != null)
              defDetail.refactorPossible = "Possible";
          else
              defDetail.refactorPossible = "Not Yet Implemented";
          ViewData.Model = defDetail;
          return View();
      }

  
  }
}
